package klog

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/klogsdk/klog-go-sdk/credentials"
	"gitee.com/klogsdk/klog-go-sdk/internal/apierr"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	getUsageFeeTimePattern = "2006-01-02"
)

type adminClient struct {
	opts *ClientOptions
	stat *Stat
}

func newAdminClient(opts *ClientOptions, stat *Stat) *adminClient {
	o := &adminClient{opts: opts, stat: stat}
	return o
}

func (o *adminClient) GetLogs(req *LogInfoReq) (*LogInfoResp, error) {
	var api = "/GetLogs"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "LogInfoReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(LogInfoResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListProjects(req *ListProjectReq) (*ListProjectResp, error) {
	var api = "/ListProjects"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListProjReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ListProjectResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListLogPools(req *ListLogPoolReq) (*ListLogPoolResp, error) {
	var api = "/ListLogPools"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListLogPoolReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ListLogPoolResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetUserStatus() (*UserInfoResp, error) {
	var api = "/GetUserStatus"
	var res = new(UserInfoResp)
	if err := o.do(api, http.MethodPost, nil, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeProjectInfo(req *NameInfo) (*DescribeProjectResp, error) {
	var api = "/DescribeProject"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DescribeProjectReq is nil", nil)
	}
	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ProjectName is null", nil)
	}
	var res = new(DescribeProjectResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeLogPool(req *NameInfo) (*DescribeLogPoolResp, error) {
	var api = "/DescribeLogPool"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DescribeProjectReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	var res = new(DescribeLogPoolResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateUser() (*ErrorResp, error) {
	var api = "/CreateUser"
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, nil, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateProject(req *ProjectInfo) (*ErrorResp, error) {
	var api = "/CreateProject"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateProjectReq is nil", nil)
	}
	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ProjectName is null", nil)
	}
	if req.Region == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "Region is null", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateLogPool(req *LogPoolInfo) (*ErrorResp, error) {
	var api = "/CreateLogPool"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateLogPoolReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	if req.Partitions <= 0 || req.Partitions > 64 {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "Partitions must be greater than 0 and less than 65", nil)
	}
	if req.RetentionDays <= 0 || req.RetentionDays > 3000 {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "RetentionDays must be greater than 0 and less than 3001", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DeleteProject(req *ProjectInfo) (*ErrorResp, error) {
	var api = "/DeleteProject"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DeleteProjectReq is nil", nil)
	}
	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ProjectName is null", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DeleteLogPool(req *LogPoolInfo) (*ErrorResp, error) {
	var api = "/DeleteLogPool"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DeleteLogPoolReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) UpdateProject(req *ProjectInfo) (*ErrorResp, error) {
	var api = "/UpdateProject"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateProjectReq is nil", nil)
	}
	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ProjectName is null", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) UpdateLogPool(req *LogPoolInfo) (*ErrorResp, error) {
	var api = "/UpdateLogPool"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateLogPoolReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) SetIndexTemplate(req *IndexTemplateReq) (*ErrorResp, error) {
	var api = "/SetIndexTemplate"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "SetIndexTemplateReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetIndexTemplate(req *NameInfo) (*IndexTemplateResp, error) {
	var api = "/GetIndexTemplate"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetIndexTemplateReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	var res = new(IndexTemplateResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetDynamicIndex(req *NameInfo) (*IndexTemplateResp, error) {
	var api = "/GetDynamicIndex"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetIndexTemplateReq is nil", nil)
	}
	if err := req.validate(); err != nil {
		return nil, err
	}
	var res = new(IndexTemplateResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateDashboard(req *DashboardInfo) (*DashboardIdResp, error) {
	var api = "/CreateDashboard"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateDashboardReq is nil", nil)
	}
	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ProjectName is null", nil)
	}
	if req.DashboardName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DashboardName is null", nil)
	}
	var res = new(DashboardIdResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListDashboards(req *ListDashboardsReq) (*ListDashboardResp, error) {
	var api = "/ListDashboards"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListDashboardsReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ListDashboardResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DeleteDashboard(req *DeleteDashboardReq) (*ErrorResp, error) {
	var api = "/DeleteDashboard"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DeleteDashboardReq is nil", nil)
	}
	if req.DashboardId == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "dashboardId is nil", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeDashboard(req *DescribeDashboardReq) (*DescribeDashboardResp, error) {
	var api = "/DescribeDashboard"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DeleteDashboardReq is nil", nil)
	}
	if req.DashboardId == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "dashboardId is nil", nil)
	}
	var res = new(DescribeDashboardResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) UpdateDashboardName(req *UpdateDashboardReq) (*ErrorResp, error) {
	var api = "/UpdateDashboardName"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateDashboardReq is nil", nil)
	}
	if req.DashboardName == "" || req.DashboardId == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateDashboardReq dashboardName or dashboardId is nil", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) UpdateDashboard(req *UpdateDashboardReq) (*ErrorResp, error) {
	var api = "/UpdateDashboard"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateDashboardReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateChart(req *ChartInfo) (*ChartIdResp, error) {
	var api = "/CreateChart"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateChartReq is nil", nil)
	}
	var res = new(ChartIdResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DeleteChart(req *ChartInfo) (*ErrorResp, error) {
	var api = "/DeleteChart"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DeleteChartReq is nil", nil)
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) UpdateChart(req *ChartInfo) (*ErrorResp, error) {
	var api = "/UpdateChart"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateChartReq is nil", nil)
	}

	if req.ChartType == "" || req.Search == nil || req.Display == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "UpdateChartReq Search or Display or ChartType param is nil", nil)
	}

	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListIamProjects() (*ListIamProjectsResp, error) {
	var api = "/ListIamProjects"
	var res = new(ListIamProjectsResp)
	if err := o.do(api, http.MethodPost, nil, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListTagKeys() (*ListTagKeyResp, error) {
	var api = "/ListTagKeys"
	var res = new(ListTagKeyResp)
	if err := o.do(api, http.MethodPost, nil, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListTagValues(req *TagInfo) (*ListTagValuesResp, error) {
	var api = "/ListTagValues"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListTagValuesReq is nil", nil)
	}
	if req.Key == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListTagValues request key is empty", nil)
	}
	var res = new(ListTagValuesResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateDownloadTask(req *DownloadTaskReq) (*ErrorResp, error) {
	var api = "/CreateDownloadTask"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateDownloadTaskReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(ErrorResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ListDownloadTasks(req *ListDownloadTaskReq) (*ListDownloadTaskResp, error) {
	var api = "/ListDownloadTasks"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ListDownloadTasksReq is nil", nil)
	}

	if err := req.PageInfo.validate(); err != nil {
		return nil, err
	}

	var res = new(ListDownloadTaskResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetDownloadUrls(req *DownloadUrlReq) (*DownloadUrlsResp, error) {
	var api = "/GetDownloadUrls"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetDownloadUrlReq is nil", nil)
	}

	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetDownloadUrlReq ProjectName is null", nil)
	}

	if req.DownloadID == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetDownloadUrlReq DownloadID is null", nil)
	}

	var res = new(DownloadUrlsResp)
	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetUsageFee(req *GetUsageFeeReq) (*GetUsageFeeResp, error) {
	var api = "/GetUsageFee"
	var res = new(GetUsageFeeResp)
	var reqInterface interface{} = req
	if req != nil {
		if err := req.ValidateReq(); err != nil {
			return nil, err
		}
	} else {
		reqInterface = nil
	}
	if err := o.do(api, http.MethodPost, reqInterface, res); err != nil {
		return nil, err
	}

	return res, nil
}

func (o *adminClient) GetToken(expireTime int64) (*GetTokenResp, error) {
	if expireTime <= 0 {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "GetToken ExpireTime must greater than 0", nil)
	}
	param := map[string]interface{}{"ExpireTime": expireTime}
	return o.getToken(param)
}

func (o *adminClient) GetTokenDefaultExpiration() (*GetTokenResp, error) {
	return o.getToken(nil)
}

func (o *adminClient) getToken(param map[string]interface{}) (*GetTokenResp, error) {
	var api = "/GetToken"
	var res = new(GetTokenResp)
	if err := o.do(api, http.MethodGet, param, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetUsage(req *GetUsageReq) (*GetUsageResp, error) {
	var api = "/GetUsage"
	var res = new(GetUsageResp)
	var reqInterface interface{} = req
	if req == nil {
		reqInterface = nil
	}
	if err := o.do(api, http.MethodPost, reqInterface, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetMonitorData(req *MonitorDataReq) (*MonitorDataResp, error) {
	var api = "/GetMonitorData"
	var res = new(MonitorDataResp)
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "MonitorDataReq is nil", nil)
	}

	if req.ProjectName == "" {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "MonitorDataReq ProjectName is null", nil)
	}

	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetDataHeat(req *DataHeatReq) (*DataHeatResp, error) {
	var api = "/GetDataHeat"
	var res = new(DataHeatResp)
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DataHeatReq is nil", nil)
	}

	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) CreateLogPolicy(req *LogPolicyInfo) (*LogPolicyResp, error) {
	var api = "/CreateLogPolicy"
	var res = new(LogPolicyResp)

	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "CreateLogPolicyReq is nil", nil)
	}

	if err := req.ValidateReq(); err != nil {
		return nil, err
	}

	if err := o.do(api, http.MethodPost, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) ModifyLogPolicy(req *LogPolicyInfo) (*LogPolicyResp, error) {
	var api = "/ModifyLogPolicy"
	var res = new(LogPolicyResp)

	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "ModifyLogPolicyReq is nil", nil)
	}

	if err := o.do(api, http.MethodPut, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeLogPolicy(req *DescribeLogPolicyReq) (*LogPolicyResp, error) {
	var api = "/DescribeLogPolicy"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DescribeLogPolicyReq is nil", nil)
	}
	if err := req.ValidateReq(); err != nil {
		return nil, err
	}
	var res = new(LogPolicyResp)
	if err := o.do(api, http.MethodGet, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) RemoveLogPolicy(req *RemoveLogPolicyReq) (*LogPolicyResp, error) {
	var api = "/RemoveLogPolicy"
	var reqInterface interface{} = req
	if req == nil {
		reqInterface = nil
	}
	var res = new(LogPolicyResp)
	if err := o.do(api, http.MethodPost, reqInterface, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeLogHistory(req *DescribeLogHistoryReq) (*LogPolicyResp, error) {
	var api = "/DescribeLogHistory"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "DescribeLogHistoryReq is nil", nil)
	}
	var res = new(LogPolicyResp)
	if err := o.do(api, http.MethodGet, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) EnableDisableLogPolicy(req *AlarmLogPolicyStatusReq) (*LogPolicyResp, error) {
	var api = "/EnableDisableLogPolicy"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "AlarmLogPolicyStatusReq is nil", nil)
	}
	var res = new(LogPolicyResp)
	if err := o.do(api, http.MethodPut, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) DescribeAlertStatistics(req *AlarmStatisticReq) (*LogPolicyResp, error) {
	var api = "/DescribeAlertStatistics"
	if req == nil {
		return nil, apierr.New(RequestOpenAPIBodyOrParamInvalid, "AlarmStatisticReq is nil", nil)
	}
	var res = new(LogPolicyResp)
	if err := o.do(api, http.MethodGet, req, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) GetUserGroup() (*UserGroupResp, error) {
	var api = "/GetUserGroup"
	var res = new(UserGroupResp)
	if err := o.do(api, http.MethodGet, nil, res); err != nil {
		return nil, err
	}
	return res, nil
}

func (o *adminClient) do(api, method string, params interface{}, res interface{}) error {
	var req *http.Request
	var err error

	switch method {
	case http.MethodPost:
		req, err = o.newPostReq(api, params)
	case http.MethodPut:
		req, err = o.newPutReq(api, params)
	case http.MethodGet:
		if params != nil {
			if c, ok := params.(mapConverter); ok {
				param := c.ConvertToMap()
				req, err = o.newGetReq(api, param)
			} else {
				req, err = o.newGetReq(api, params.(map[string]interface{}))
			}
		} else {
			req, err = o.newGetReq(api, nil)
		}
	default:
		req, err = o.newPostReq(api, params)
	}

	if err != nil {
		return err
	}
	bsP, sc, err := o.retryDoReq(req)
	if err != nil {
		return err
	}

	defer func() {
		iReq := res.(iRequestId)
		iReq.SetRequestId(req.Header.Get(HeaderRequestId))
		iStatus := res.(iStatusCode)
		iStatus.SetStatusCode(sc)
	}()

	if isKOPUrl(req.URL.Host) {
		oer := new(openAPIErrorResp)
		if err = json.Unmarshal(*bsP, oer); err != nil {
			return apierr.New(JsonUnMarshalError, err.Error(), err)
		}
		if oer.hasError() {
			errorSetter := res.(IKLogError)
			errorSetter.SetErrorResp(oer.convert2KlogResp())
			return nil
		}
	}
	if len(*bsP) > 0 {
		if err = json.Unmarshal(*bsP, res); err != nil {
			return apierr.New(JsonUnMarshalError, err.Error(), err)
		}
	}
	return nil
}

func (o *adminClient) newPostReq(api string, body interface{}) (*http.Request, error) {
	return o.newReq(http.MethodPost, api, body)
}

func (o *adminClient) newPutReq(api string, body interface{}) (*http.Request, error) {
	return o.newReq(http.MethodPut, api, body)
}

func (o *adminClient) newGetReq(api string, params map[string]interface{}) (*http.Request, error) {
	b := strings.Builder{}
	b.WriteString(api)
	var paramLen, idx = len(params), 0
	if params != nil && paramLen != 0 {
		for k, v := range params {
			if idx == 0 {
				b.WriteRune('?')
			}
			b.WriteString(k)
			b.WriteString("=")
			b.WriteString(fmt.Sprintf("%v", v))
			idx++
			if idx != paramLen {
				b.WriteRune('&')
			}
		}
	}
	return o.newReq(http.MethodGet, b.String(), nil)
}

func (o *adminClient) newReq(method, api string, body interface{}) (*http.Request, error) {
	var reader = new(bytes.Reader)
	if body == nil {
		reader = bytes.NewReader([]byte("{}"))
	} else {
		b, err := json.Marshal(body)
		if err != nil {
			return nil, apierr.New(JsonMarshalError, err.Error(), err)
		}
		reader = bytes.NewReader(b)
	}

	req, err := http.NewRequest(strings.ToUpper(method), makeApiEndPoint(o.opts.Endpoint, api), reader)
	if err != nil {
		return nil, apierr.New(InvalidRequest, fmt.Sprintf("make request error=%s", err.Error()), err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set(HeaderRequestId, newUUID().String())
	req.Header.Set("Accept", "application/json")
	if isKOPUrl(req.URL.Host) {
		// 无论值是多少，只要有这个请求头则会对请求体做 sha256
		req.Header.Set("Content-MD5", "1")
	}

	if err = signatureV4(o.cred(), "inner", req, reader); err != nil {
		return nil, apierr.New(SignatureError, err.Error(), err)
	}
	return req, nil
}

func (o *adminClient) doReq(req *http.Request) (*[]byte, int, error) {
	requestId := req.Header.Get(HeaderRequestId)
	o.logger().Debugf("adminClient.doReq request uri: [%s] requestId:[%s]", req.URL.RequestURI(), requestId)
	resp, err := o.opts.HTTPClient.Do(req)
	if err != nil {
		return nil, SDKInternalErrorStatusCode, apierr.New(HttpError, err.Error(), err)
	}
	defer resp.Body.Close()
	bs, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		if errors.Is(err, io.EOF) {
			return &[]byte{}, resp.StatusCode, nil
		}
		return nil, resp.StatusCode, apierr.New(HttpError, fmt.Sprintf("read response from:[%s] error=%s", req.URL.RequestURI(), err.Error()), err)
	}
	return &bs, resp.StatusCode, nil
}

func (o *adminClient) retryDoReq(req *http.Request) (*[]byte, int, error) {
	var res *[]byte
	var err error
	var sc = SDKInternalErrorStatusCode
	var retryTimes = 0
	for true {
		if retryTimes > o.opts.MaxRetries {
			break
		}
		res, sc, err = o.doReq(req)
		if err == nil {
			break
		}
		retryTimes++
		o.stat.addRetried(1)
		o.stat.setLastRetried(retryTimes)
		time.Sleep(time.Duration(retryTimes*100) * time.Millisecond)
	}
	return res, sc, err
}

func (o *adminClient) logger() Logger {
	return o.opts.Logger
}

func (o *adminClient) cred() *credentials.Credentials {
	return o.opts.Credentials
}

func makeApiEndPoint(u, api string) string {
	// 简单方案
	if isKOPUrl(u) {
		uu, _ := url.Parse(api)
		values := url.Values{}
		values.Add("Action", uu.Path[1:])
		values.Add("Version", OpenAPIVersion)
		for k := range uu.Query() {
			values.Add(k, uu.Query().Get(k))
		}
		return fmt.Sprintf("%s?%s", u, values.Encode())
	}
	return u + api
}

func isKOPUrl(u string) bool {
	return strings.Contains(u, "api")
}
